/*
 * Copyright 2001-2005 (C) MetaStuff, Ltd. All Rights Reserved.
 *
 * This software is open source.
 * See the bottom of this file for the licence.
 */

package org.dom4j.tree;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.WeakHashMap;

import org.dom4j.DocumentFactory;
import org.dom4j.Namespace;
import org.dom4j.QName;

/**
 * <p> <code>QNameCache</code> caches instances of <code>QName</code> for
 * reuse both across documents and within documents. </p>< < < < < < <
 * QNameCache.java
 * 
 * @author <a href="mailto:james.strachan@metastuff.com">James Strachan </a>
 * @version $Revision: 1.2 $ =======
 * 
 * @author <a href="mailto:james.strachan@metastuff.com">James Strachan </a>
 * @version $Revision: 1.2 $ >>>>>>> 1.15
 */
public class QNameCache
{
   /** Cache of {@link QName}instances with no namespace */
   protected Map noNamespaceCache =
      Collections.synchronizedMap(new WeakHashMap());

   /**
    * Cache of {@link Map}instances indexed by namespace which contain caches
    * of {@link QName}for each name
    */
   protected Map namespaceCache =
      Collections.synchronizedMap(new WeakHashMap());

   /**
    * The document factory associated with new QNames instances in this cache or
    * null if no instances should be associated by default
    */
   private DocumentFactory documentFactory;

   public QNameCache()
   {
   }

   public QNameCache(DocumentFactory documentFactory)
   {
      this.documentFactory = documentFactory;
   }

   /**
    * Returns a list of all the QName instances currently used
    * 
    * @return DOCUMENT ME!
    */
   public List getQNames()
   {
      List answer = new ArrayList();
      answer.addAll(noNamespaceCache.values());

      for(Iterator it = namespaceCache.values().iterator(); it.hasNext();)
      {
         Map map = (Map) it.next();
         answer.addAll(map.values());
      }

      return answer;
   }

   /**
    * DOCUMENT ME!
    * 
    * @param name DOCUMENT ME!
    * 
    * @return the QName for the given name and no namepsace
    */
   public QName get(String name)
   {
      QName answer = null;

      if(name != null)
      {
         answer = (QName) noNamespaceCache.get(name);
      }
      else
      {
         name = "";
      }

      if(answer == null)
      {
         answer = createQName(name);
         answer.setDocumentFactory(documentFactory);
         noNamespaceCache.put(name, answer);
      }

      return answer;
   }

   /**
    * DOCUMENT ME!
    * 
    * @param name DOCUMENT ME!
    * @param namespace DOCUMENT ME!
    * 
    * @return the QName for the given local name and namepsace
    */
   public QName get(String name, Namespace namespace)
   {
      Map cache = getNamespaceCache(namespace);
      QName answer = null;

      if(name != null)
      {
         answer = (QName) cache.get(name);
      }
      else
      {
         name = "";
      }

      if(answer == null)
      {
         answer = createQName(name, namespace);
         answer.setDocumentFactory(documentFactory);
         cache.put(name, answer);
      }

      return answer;
   }

   /**
    * DOCUMENT ME!
    * 
    * @param localName DOCUMENT ME!
    * @param namespace DOCUMENT ME!
    * @param qName DOCUMENT ME!
    * 
    * @return the QName for the given local name, qualified name and namepsace
    */
   public QName get(String localName, Namespace namespace, String qName)
   {
      Map cache = getNamespaceCache(namespace);
      QName answer = null;

      if(localName != null)
      {
         answer = (QName) cache.get(localName);
      }
      else
      {
         localName = "";
      }

      if(answer == null)
      {
         answer = createQName(localName, namespace, qName);
         answer.setDocumentFactory(documentFactory);
         cache.put(localName, answer);
      }

      return answer;
   }

   public QName get(String qualifiedName, String uri)
   {
      int index = qualifiedName.indexOf(':');

      if(index < 0)
      {
         return get(qualifiedName, Namespace.get(uri));
      }
      else
      {
         String name = qualifiedName.substring(index + 1);
         String prefix = qualifiedName.substring(0, index);

         return get(name, Namespace.get(prefix, uri));
      }
   }

   /**
    * DOCUMENT ME!
    * 
    * @param qname DOCUMENT ME!
    * 
    * @return the cached QName instance if there is one or adds the given qname
    *         to the cache if not
    */
   public QName intern(QName qname)
   {
      return get(qname.getName(), qname.getNamespace(), qname
         .getQualifiedName());
   }

   /**
    * DOCUMENT ME!
    * 
    * @param namespace DOCUMENT ME!
    * 
    * @return the cache for the given namespace. If one does not currently exist
    *         it is created.
    */
   protected Map getNamespaceCache(Namespace namespace)
   {
      if(namespace == Namespace.NO_NAMESPACE)
      {
         return noNamespaceCache;
      }

      Map answer = null;

      if(namespace != null)
      {
         answer = (Map) namespaceCache.get(namespace);
      }

      if(answer == null)
      {
         answer = createMap();
         namespaceCache.put(namespace, answer);
      }

      return answer;
   }

   /**
    * A factory method
    * 
    * @return a newly created {@link Map}instance.
    */
   protected Map createMap()
   {
      return Collections.synchronizedMap(new HashMap());
   }

   /**
    * Factory method to create a new QName object which can be overloaded to
    * create derived QName instances
    * 
    * @param name DOCUMENT ME!
    * 
    * @return DOCUMENT ME!
    */
   protected QName createQName(String name)
   {
      return new QName(name);
   }

   /**
    * Factory method to create a new QName object which can be overloaded to
    * create derived QName instances
    * 
    * @param name DOCUMENT ME!
    * @param namespace DOCUMENT ME!
    * 
    * @return DOCUMENT ME!
    */
   protected QName createQName(String name, Namespace namespace)
   {
      return new QName(name, namespace);
   }

   /**
    * Factory method to create a new QName object which can be overloaded to
    * create derived QName instances
    * 
    * @param name DOCUMENT ME!
    * @param namespace DOCUMENT ME!
    * @param qualifiedName DOCUMENT ME!
    * 
    * @return DOCUMENT ME!
    */
   protected QName createQName(String name, Namespace namespace,
      String qualifiedName)
   {
      return new QName(name, namespace, qualifiedName);
   }
}

/*
 * Redistribution and use of this software and associated documentation
 * ("Software"), with or without modification, are permitted provided that the
 * following conditions are met:
 * 
 * 1. Redistributions of source code must retain copyright statements and
 * notices. Redistributions must also contain a copy of this document.
 * 
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 * 
 * 3. The name "DOM4J" must not be used to endorse or promote products derived
 * from this Software without prior written permission of MetaStuff, Ltd. For
 * written permission, please contact dom4j-info@metastuff.com.
 * 
 * 4. Products derived from this Software may not be called "DOM4J" nor may
 * "DOM4J" appear in their names without prior written permission of MetaStuff,
 * Ltd. DOM4J is a registered trademark of MetaStuff, Ltd.
 * 
 * 5. Due credit should be given to the DOM4J Project - http://www.dom4j.org
 * 
 * THIS SOFTWARE IS PROVIDED BY METASTUFF, LTD. AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL METASTUFF, LTD. OR ITS CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 * 
 * Copyright 2001-2005 (C) MetaStuff, Ltd. All Rights Reserved.
 */